﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ApprovalEngine.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the ApprovalEngine type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace BusinessEngines
{
  using System;
  using System.Collections.Generic;
  using BusinessEngineInterfaces;
  using Entities;
  using Exceptions;
  using ServiceInterfaces;
  using UnityRepository.Interfaces;

  public class ApprovalEngine : IApprovalEngine
  {
    private readonly IDocumentService documentService;
    private readonly IApprovalRepository approvalRepository;

    public ApprovalEngine(IDocumentService documentService, IApprovalRepository approvalRepository)
    {
      this.documentService = documentService;
      this.approvalRepository = approvalRepository;
    }

    private enum ApprovalResult
    {
      Approvable,
      AlreadyApproved,
      AlreadyRejected
    }

    public void AutoApproveDocument(String documentId)
    {
      var document = this.documentService.GetDocument(documentId);
      this.PerformApproval(document, "auto approval");
    }

    public void ApproveDocument(String userName, String documentId)
    {
      Document document = this.GetDocument(documentId);

      ApprovalResult approvalResult = this.CanDocumentBeApproved(document);

      switch (approvalResult)
      {
        case ApprovalResult.Approvable:
          this.PerformApproval(document, userName);
          break;
        case ApprovalResult.AlreadyApproved:
          throw new DocumentAlreadyApprovedException("Document is already approved");
        case ApprovalResult.AlreadyRejected:
          throw new DocumentAlreadyRejectedException("Document is already rejected");
      }
    }

    public BatchApprovalResults ApproveGrid(String grid, String userName)
    {
      IList<Document> gridDocuments = this.documentService.GetDocumentsWithApprovalAndRejectionEntities(grid);

      var batchApprovalResults = new BatchApprovalResults();
      var approvalItems = new List<Approval>();

      foreach (Document document in gridDocuments)
      {
        this.AttemptApprovalAndAddResultsToLists(userName, document, batchApprovalResults, approvalItems);
      }

      this.approvalRepository.Create(approvalItems);

      return batchApprovalResults;
    }

    public BatchApprovalResults ApproveBasket(String userName, String cartId)
    {
      IList<Document> basketDocuments = this.documentService.GetDocumentsForBasket(cartId);

      var batchApprovalResults = new BatchApprovalResults();
      var approvalItems = new List<Approval>();

      foreach (Document document in basketDocuments)
      {
        this.AttemptApprovalAndAddResultsToLists(userName, document, batchApprovalResults, approvalItems);
      }

      this.approvalRepository.Create(approvalItems);

      return batchApprovalResults;
    }

    private void PerformApproval(Document document, String username)
    {
      var approval = new Approval { ApprovedBy = username, ApprovedDate = DateTime.Now, DocumentId = document.Id, };
      this.approvalRepository.Create(approval);
    }

    private Document GetDocument(String documentId)
    {
      Document document = this.documentService.GetDocument(documentId);
      if (document == null)
      {
        throw new UnableToRetrieveDocumentException(String.Format("Unable to retrieve document {0}", documentId));
      }

      return document;
    }

    private void AttemptApprovalAndAddResultsToLists(
      String userName,
      Document document,
      BatchApprovalResults batchApprovalResults,
      ICollection<Approval> approvalItems)
    {
      ApprovalResult approvalResult = this.CanDocumentBeApproved(document);

      switch (approvalResult)
      {
        case ApprovalResult.Approvable:
          batchApprovalResults.ApprovedDocuments.Add(document.DocumentId);
          approvalItems.Add(new Approval { ApprovedBy = userName, ApprovedDate = DateTime.Now, DocumentId = document.Id });
          break;
        case ApprovalResult.AlreadyApproved:
          batchApprovalResults.AlreadyApprovedDocuments.Add(document.DocumentId);
          break;
        case ApprovalResult.AlreadyRejected:
          batchApprovalResults.AlreadyRejectedDocuments.Add(document.DocumentId);
          break;
      }
    }

    private ApprovalResult CanDocumentBeApproved(Document document)
    {
      if (document.Approval != null)
      {
        return ApprovalResult.AlreadyApproved;
      }

      if (document.Rejection != null)
      {
        return ApprovalResult.AlreadyRejected;
      }

      return ApprovalResult.Approvable;
    }
  }
}
